package com.codeyang.config.shiro;

import com.codeyang.domain.ActiveUser;
import com.codeyang.domain.User;
import com.codeyang.service.*;
import com.codeyang.vo.MenuTreeVo;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

import java.util.List;

/**
 * 描述: 改造|接管shiro的 鉴权与认证
 * 自定义realm去匹配用户名和密码
 *
 * @author CodeYang_Site
 * @version 2021/4/20 15:36
 */
public class UserRealm extends AuthorizingRealm {


    @Autowired
    @Lazy
    private UserService userService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private RoleService roleService;

    /**
     * 鉴权器--
     *
     * @param principalCollection 做授权
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    /**
     * 做认证-登录认证
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        // 1 得到 用户登录名  -- 使用的是手机号登录
        String userLoginName = token.getPrincipal().toString();
        // 2 根据 用户登录名 查询用户是否存在
        User user = userService.findUserByUserName(userLoginName);

        if (user == null) {
            // 让 shiro 自己去处理为空的异常
            return null;
        }
        System.out.println(user);
        System.out.println("查询后的密码---" + user.getPassword());

        // 登录对象  密码=凭证  盐=字节码文件 用户名称
        //public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt,String realmName)
        // 3 下一步是查询后的密码 密码的匹配器,做密码的校验  -- 交给我们自己的密码匹配器
        //以下方法只执行一次
        ActiveUser activeUser = new ActiveUser();
        activeUser.setUser(user);
        Long userId = user.getUserId();
        // 角色处理
        activeUser.setRoles(getUserRoles(userId));
        // 权限处理
        activeUser.setPermissions(getUsePermissions(userId, activeUser.getRoles().contains("role:admin")));
        // 菜单处理
        activeUser.setMenuTreeVos(getUseMenus(userId, activeUser.getRoles().contains("role:admin")));
        // 存储到shiro中 --- > shiroDao ---> redis 中
        return new SimpleAuthenticationInfo(activeUser, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), user.getUserName());


    }

    /**
     * 根据用户id查询菜单数据
     *
     * @param userId
     * @return
     */
    private List<MenuTreeVo> getUseMenus(Long userId, boolean isAdmin) {
        return menuService.getMenuByUserId(userId, isAdmin);
    }

    /**
     * 根据用户id查询权限
     *
     * @param userId
     * @return
     */
    private List<String> getUsePermissions(Long userId, boolean isAdmin) {
        // RBAC 模式: 用户和资源的问题  资源== 菜单+权限(按钮)--权限的本质也是菜单的一种
        return menuService.getUsePermissions(userId, isAdmin);
    }

    /**
     * 根据用户id查询角色
     *
     * @param userId
     * @return
     */
    private List<String> getUserRoles(Long userId) {
        // 使用用户的id 查询对应的角色的code值
        List<String> roleCodes = roleService.getUserRolesCode(userId);
        return roleCodes;
    }


}
